package com.sirui.mongo.file;

import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import com.sirui.spider.kuai.entity.CommonConstants;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;

import java.io.*;
import java.util.List;


@Slf4j
@Service
public class MongoGridFSService {

    // GridFS下的bucket，自行指定要把文件存储到哪个 bucket。
    public static String BUCKET_NAME = "images";
    // 源文件，即要被存储的文件的绝对路径
    public static String FILE_PATH = "C:\\Users\\linson\\Pictures\\01AA.jpg";
    // 存储文件后自动生成的存储文件信息的collection，一般是xx.files。
    public static String COLLECTION_NAME = "images.files";
    // 用于演示接收输出文件的路径
    public static String FILE_OUTPUT_PATH = "C:\\Users\\linson\\Desktop\\";
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 传入bucketName得到指定bucket操作对象。
     *
     * @param bucketName
     * @return
     */
    public GridFSBucket createGridFSBucket(String bucketName) {
        MongoDatabase db = mongoTemplate.getDb();
        return GridFSBuckets.create(db, bucketName);
    }

    /**
     * 储存文件到GridFS
     */
    public ObjectId saveToGridFS() {
        // 先调用上面方法得到一个GridFSBucket的操作对象
        GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
        File file = new File(FILE_PATH);
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 设置GridFS存储配置，这里是设置了每个chunk(块)的大小为1024个字节，也可以设置大一点。 MetaData是对文件的说明，如果不需要可以不写。 也是以键值对存在，BSON格式。
        GridFSUploadOptions options = new GridFSUploadOptions().chunkSizeBytes(1024).metadata(new Document("user", "linson1"));
        // 调用GridFSBucket中的uploadFromStream方法，把对应的文件流传递进去，然后就会以binary(二进制格式)存储到GridFS中，并得到一个文件在xx.files中的主键ID，后面可以用这个ID来查找关联的二进制文件数据。
        ObjectId objectId = gridFSBucket.uploadFromStream(file.getName(), inputStream, options);
        System.out.println(file.getName() + "已存入mongodb gridFS, 对应id是：" + objectId);
        return objectId;
    }

    public ObjectId saveToGridFS(String bucketName, File file, Document document) {
        // 先调用上面方法得到一个GridFSBucket的操作对象
        GridFSBucket gridFSBucket = createGridFSBucket(bucketName);
        //File file = new File(FILE_PATH);
        FileInputStream inputStream = null;
        ObjectId objectId = null;
        try {
            inputStream = new FileInputStream(file);
            // 设置GridFS存储配置，这里是设置了每个chunk(块)的大小为1024个字节，也可以设置大一点。 MetaData是对文件的说明，如果不需要可以不写。 也是以键值对存在，BSON格式。
            GridFSUploadOptions options = new GridFSUploadOptions().chunkSizeBytes(1024).metadata(document);
            // 调用GridFSBucket中的uploadFromStream方法，把对应的文件流传递进去，然后就会以binary(二进制格式)存储到GridFS中，并得到一个文件在xx.files中的主键ID，后面可以用这个ID来查找关联的二进制文件数据。
            objectId = gridFSBucket.uploadFromStream(file.getName(), inputStream, options);
            System.out.println(file.getName() + "已存入mongodb gridFS, 对应id是：" + objectId);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return objectId;
    }

    /**
     * 从GridFS中读取文件
     */
    public void readFromGridFS() {
        // 这里查找条件我先不写，默认查所有，取第一条做验证演示。 用Document类接收。
        List<Document> files = mongoTemplate.find(new Query(), Document.class, COLLECTION_NAME);
        Document file = files.get(0);
        // 得到主键ID，作为等下要查询的文件ID值。
        ObjectId fileId = file.getObjectId("_id");
        String filename = file.getString("filename");
        // 先调用上面方法得到一个GridFSBucket的操作对象
        GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
        // 调用openDownloadStream方法得到文件IO流。
        InputStream downloadStream = gridFSBucket.openDownloadStream(fileId);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(FILE_OUTPUT_PATH + filename);
            // 把IO流直接到指定路径的输出流对象实现输出。
            FileCopyUtils.copy(downloadStream, fileOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void readFromGridFS(String key, String value) {
        // 这里查找条件我先不写，默认查所有，取第一条做验证演示。 用Document类接收。
        Query query = new Query(Criteria.where(key).is(value));
        List<Document> files = mongoTemplate.find(query, Document.class, COLLECTION_NAME);
        if (files.size() > 0) {
            Document file = files.get(0);
            log.info(String.format("A@@A [%s]", file));
            // 得到主键ID，作为等下要查询的文件ID值。
            ObjectId fileId = file.getObjectId("_id");
            String filename = file.getString("filename");
            // 先调用上面方法得到一个GridFSBucket的操作对象
            GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
            // 调用openDownloadStream方法得到文件IO流。
            InputStream downloadStream = gridFSBucket.openDownloadStream(fileId);
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(FILE_OUTPUT_PATH + filename);
                // 把IO流直接到指定路径的输出流对象实现输出。
                FileCopyUtils.copy(downloadStream, fileOutputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            log.info(String.format("A@@A [%s]", "未查询到" + key + ":" + value));
        }

    }

    public void readFromGridFS(Query query) {
        List<Document> files = mongoTemplate.find(query, Document.class, COLLECTION_NAME);
        log.info(String.format("A@@A [%s]", files));
    }

    public InputStream readFromGridFSByMetadata(String key, String value) {

        //Query query = new Query(Criteria.where("metadata.objId").is("17"));
        //Query query = new Query(Criteria.where(key).is(value));

        Criteria criteria = new Criteria();
        criteria.and(key).is(value);
        criteria.and("metadata.index").is("0");
        Query query = new Query(criteria);

        List<Document> files = mongoTemplate.find(query, Document.class, COLLECTION_NAME);
        if (files.size() > 0) {
            Document file = files.get(0);
            ObjectId fileId = file.getObjectId("_id");
            GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
            InputStream downloadStream = gridFSBucket.openDownloadStream(fileId);
            return downloadStream;
        }
        return null;
    }

    public InputStream readFromGridFSByCriteria(Criteria criteria) {
        List<Document> files = mongoTemplate.find(new Query(criteria), Document.class, COLLECTION_NAME);
        if (files.size() > 0) {
            Document file = files.get(0);
            ObjectId fileId = file.getObjectId("_id");
            GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
            InputStream downloadStream = gridFSBucket.openDownloadStream(fileId);
            return downloadStream;
        }
        return null;
    }

    public void downFile(Criteria criteria) {
        List<Document> files = mongoTemplate.find(new Query(criteria), Document.class, COLLECTION_NAME);
        if (files.size() > 0) {
            Document file = files.get(0);
            ObjectId fileId = file.getObjectId("_id");
            String filename = file.getString("filename");
            GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
            InputStream downloadStream = gridFSBucket.openDownloadStream(fileId);
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(FILE_OUTPUT_PATH + filename);
                // 把IO流直接到指定路径的输出流对象实现输出。
                FileCopyUtils.copy(downloadStream, fileOutputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Transactional(readOnly = false, value = CommonConstants.Config_transactionManager_Name)
    public void deleteImgObjectByObjIds(String[] ids, String collectionName) {
        GridFSBucket gridFSBucket = createGridFSBucket(BUCKET_NAME);
        //List<String> collect = Arrays.stream(ids).map(String::valueOf).collect(Collectors.toList());
        Criteria criteria = Criteria.where("metadata.objId").in(ids);
        Query query = new Query(criteria);
        List<Document> files = mongoTemplate.find(query, Document.class, collectionName);
        files.stream().forEach(document -> {
            ObjectId objectId = document.getObjectId("_id");
            log.info(String.format("A@@A [%s]", objectId.toString()));
            gridFSBucket.delete(objectId);
        });
    }
}
